package be.kuleuven.cs.mop.app.controllers;


import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;

import be.kuleuven.cs.mop.app.System;
import be.kuleuven.cs.mop.domain.exceptions.InvalidCurrentUserException;
import be.kuleuven.cs.mop.domain.exceptions.TaskManagerException;
import be.kuleuven.cs.mop.domain.model.Invitation;
import be.kuleuven.cs.mop.domain.model.InvitationManager;
import be.kuleuven.cs.mop.domain.model.Task;
import be.kuleuven.cs.mop.domain.model.User;
import be.kuleuven.cs.mop.domain.model.UserType;
import be.kuleuven.cs.mop.domain.model.impl.InvitationImpl;
import be.kuleuven.cs.mop.domain.model.impl.TaskImpl;
import be.kuleuven.cs.mop.domain.model.impl.UserImpl;
import be.kuleuven.cs.mop.domain.model.impl.UserTypeImpl;



/**
 * The {@link Controller} responsible for handling {@link User} operations
 */
public class UserController extends Controller {
	
	public UserController(final System system) {
		super(system);
	}
	
	
	
	/**
	 * Adjusts the current time of the system
	 * @throws InvalidCurrentUserException NO_USER_LOGGED_IN - {@link #checkIfCurrentUserIsAdmin}
	 * @throws InvalidCurrentUserException USER_NOT_QUALIFIED_TO_EXECUTE - {@link #checkIfCurrentUserIsAdmin}
	 */
	public void adjustClockValue(Calendar calender) throws InvalidCurrentUserException {
		checkIfCurrentUserIsAdmin();
		getWorld().getClock().setTime(calender);
	}
	
	/**
	 * Creates a new User and adds it to the system
	 * 
	 * @param name - the desired name for the User
	 * @param type - the desired UserType
	 * 
	 * @return the created User
	 * 
	 * @throws TaskManagerException USER_INVALID_NAME - !{@link UserImpl#isValidName(String name)}
	 * @throws TaskManagerException INVALID_USER_TYPE - !{@link UserImpl#isValidUserType(UserTypeImpl type)}
	 * @throws InvalidCurrentUserException NO_USER_LOGGED_IN - {@link #checkIfCurrentUserIsAdmin}
	 * @throws InvalidCurrentUserException USER_NOT_QUALIFIED_TO_EXECUTE - {@link #checkIfCurrentUserIsAdmin}
	 */
	public User createUser(final String name, final UserType type) throws TaskManagerException, InvalidCurrentUserException {
		checkIfCurrentUserIsAdmin();
		return new UserImpl(getWorld(), name, (UserTypeImpl)type);
	}
	
	/**
	 * Returns the current {@link User} 
	 * @throws InvalidCurrentUserException NO_USER_LOGGED_IN - {@link #getCurrentUser()}
	 */
	public User getCurrentLoggedInUser() throws InvalidCurrentUserException {
		return getCurrentUser();
	}
	
	/**
	 * Returns the current time of the system
	 * @throws InvalidCurrentUserException NO_USER_LOGGED_IN - {@link #checkIfUserIsLoggedIn()}
	 */
	public Calendar getTime() throws InvalidCurrentUserException {
		checkIfUserIsLoggedIn();
		return getWorld().getClock().getTime();
	}
	
	/**
	 * Returns a list of all existing {@link User}s
	 */
	public List<User> getUsers() {
		return new ArrayList<User>(getWorld().getUsers());
	}
	
	/**
	 * Returns a list of all existing/loaded {@link UserType}s
	 * @throws InvalidCurrentUserException NO_USER_LOGGED_IN - {@link #checkIfUserIsLoggedIn()}
	 */
	public List<UserType> getUserTypes() throws InvalidCurrentUserException {
		checkIfUserIsLoggedIn();
		return new ArrayList<UserType>(getWorld().getUserTypes());
	}
	
	/**
	 * Sets the current User of the system
	 * @param user - a User
	 * 
	 * @throws TaskManagerException INVALID_USER - !{@link System#isValidUser(UserImpl user)}
	 */
	public void setUser(final User user) throws TaskManagerException {
		getSystem().setUser((UserImpl)user);
	}
	
	/**
	 * Accept a invitation.
	 * @param invitation - an Invitation
	 * 
	 * @throws InvalidCurrentUserException NO_USER_LOGGED_IN - {@link #checkIfUserIsLoggedIn()}
	 * @throws TaskManagerException NOT_ALLOWED-TO_MODIFY_INVITATION
	 */
	public void acceptUserInvitation(Invitation invitation)
			throws TaskManagerException, InvalidCurrentUserException {
		checkIfUserIsLoggedIn();
		getWorld().getInvitationManager().accept(
				(InvitationImpl)invitation,
				getCurrentUser());
	}
	
	/**
	 * Decline a invitation.
	 * 
	 * @param invitation - an Invitation
	 * 
	 * @throws InvalidCurrentUserException NO_USER_LOGGED_IN - {@link #checkIfUserIsLoggedIn()}
	 * @throws TaskManagerException NOT_ALLOWED-TO_MODIFY_INVITATION
	 */
	public void declineUserInvitation(Invitation invitation)
			throws InvalidCurrentUserException, TaskManagerException {
		checkIfUserIsLoggedIn();
		getWorld().getInvitationManager().reject(
				(InvitationImpl)invitation,
				getCurrentUser());
	}
	
	/**
	 * Adds a {@link User} invitation for a {@link Task}
	 * 
	 * @param task - a Task
	 * @param user - a User
	 * 
	 * @throws TaskManagerException USER_ALREADY_INVITED
	 * @throws TaskManagerException USER_OWNS_THIS_TASK
	 * @throws TaskManagerException TASK_INVITATION_MAXIMUM
	 * @throws TaskManagerException INVALID_USER_TYPE_HELPER
	 * @throws InvalidCurrentUserException NO_USER_LOGGED_IN - {@link #checkIfUserIsLoggedIn()}
	 * @throws InvalidCurrentUserException USER_NOT_OWNER_OF_TASK - The current User is not the owner of the given task to be modified.
	 */
	public void addUserInvitation(Task task, User user) throws TaskManagerException, InvalidCurrentUserException {
		checkIfUserIsLoggedIn();
		if(!task.getUser().equals(getCurrentUser())){
			throw new InvalidCurrentUserException(InvalidCurrentUserException.USER_NOT_OWNER_OF_TASK);
		}
		getInvitationManager().invite((UserImpl)user, (TaskImpl)task);
	}
	
	/**
	 * Logs out the currently logged in user, if any
	 */
	public void logout() {
		getSystem().logout();
	}
	
	/**
	 * Removes a {@link User} invitation for a {@link Task}
	 * @throws InvalidCurrentUserException NO_USER_LOGGED_IN - {@link #checkIfUserIsLoggedIn()}
	 * @throws InvalidCurrentUserException USER_NOT_OWNER_OF_TASK - The current User is not the owner of the given task to be modified.
	 */
	public void removeUserInvitation(final Invitation invitation) throws InvalidCurrentUserException{
		checkIfUserIsLoggedIn();
		if(!invitation.getTask().getUser().equals(getCurrentUser())){
			throw new InvalidCurrentUserException(InvalidCurrentUserException.USER_NOT_OWNER_OF_TASK);
		}
		getInvitationManager().remove((InvitationImpl)invitation);
	}
	
	/**
	 * Returns the list of pending invitations for Tasks owned by other Users.
	 * @return the list of pending Invitations
	 * @throws InvalidCurrentUserException NO_USER_LOGGED_IN - {@link #checkIfUserIsLoggedIn()}
	 */
	public List<Invitation> getPendingInvitations() throws InvalidCurrentUserException {
		checkIfUserIsLoggedIn();
		return new ArrayList<Invitation>(getWorld().getInvitationManager().getInvitationsPending(getCurrentUser()));
	}
	
	/**
	 * Returns the list of invitations for a Task.
	 * @return the list of pending Invitations
	 * @throws InvalidCurrentUserException NO_USER_LOGGED_IN - {@link #checkIfUserIsLoggedIn()}
	 */
	public List<Invitation> getInvitations(final Task task) throws InvalidCurrentUserException {
		checkIfUserIsLoggedIn();
		return new ArrayList<Invitation>(getWorld().getInvitationManager().getInvitations(task));
	}
	
	
	/**
	 * Returns the list of accepted invitations for Tasks owned by other Users.
	 * @return the list of accepted Invitations
	 * @throws InvalidCurrentUserException NO_USER_LOGGED_IN - {@link #checkIfUserIsLoggedIn()}
	 */
	public List<Invitation> getAcceptedInvitations() throws InvalidCurrentUserException {
		checkIfUserIsLoggedIn();
		return new ArrayList<Invitation>(getWorld().getInvitationManager().getInvitationsAccepted(getCurrentUser()));	
	}
	
	/**
	 * Returns the global {@link InvitationManager}
	 */
	private InvitationManager getInvitationManager() {
		return getWorld().getInvitationManager();
	}
	
}